<template>
  <div class="script-source-of-template">
    <jb-form-item
      v-if="isScriptRefer"
      ref="scriptId"
      :label="$t('脚本引用')"
      property="scriptId"
      required
      :rules="rules">
      <div class="refer-script-item">
        <compose-form-item
          class="form-item-content"
          type="select">
          <bk-select
            :clearable="false"
            style="width: 120px;"
            :value="referType"
            @change="handleReferScriptTypeChange">
            <bk-option
              :id="2"
              :name="$t('业务脚本')">
              {{ $t('业务脚本') }}
            </bk-option>
            <bk-option
              :id="3"
              :name="$t('公共脚本')">
              {{ $t('公共脚本') }}
            </bk-option>
          </bk-select>
          <bk-select
            ref="scriptSelect"
            :clearable="false"
            :placeholder="$t('选择引用脚本')"
            searchable
            style="width: 375px;"
            :value="formData[scriptVersionIdField]"
            @change="handleScriptVersionIdChange">
            <component
              :is="scriptGroupComponent"
              v-for="(group, index) in scriptListDisplay"
              :key="index"
              :name="index === 0 ? $t('当前脚本') : $t('其它脚本')">
              <auth-option
                v-for="(option, itemIndex) in group"
                :id="option.scriptVersionId"
                :key="`${option.scriptVersionId}_${itemIndex}`"
                :auth="authView"
                :name="option.name"
                :permission="option.canView"
                :resource-id="option.id" />
            </component>
            <template slot="extension">
              <auth-component :auth="authCreate">
                <div
                  style="cursor: pointer;"
                  @click="handleGoCreate">
                  <i class="bk-icon icon-plus-circle mr10" />{{ newBtnText }}
                </div>
                <div slot="forbid">
                  <i class="bk-icon icon-plus-circle mr10" />{{ newBtnText }}
                </div>
              </auth-component>
            </template>
          </bk-select>
        </compose-form-item>
        <icon
          v-if="formData[scriptStatusField]"
          class="update-flag"
          :tippy-tips="$t('引用脚本待更新')"
          type="script-update" />
        <div
          v-if="formData[scriptVersionIdField]"
          class="refer-script-detail"
          :tippy-tips="$t('脚本详情')"
          @click="handleGoScriptDetail">
          <icon type="jump" />
        </div>
      </div>
    </jb-form-item>
    <!-- 新增修改字段  -->
    <div>
      <jb-form-item
        v-if="isScriptRefer"
        ref="command"
        label="工作目录"
        property="command"
        required
        :rules="commandRules">
        <div class="refer-script-item">
          <bk-select
            class="work_space_script_type-select"
            :clearable="false"
            style="width: 140px;"
            :value="workSpaceScriptType"
            @change="handleWorkSpaceScriptTypeChange">
            <template v-for="item in scriptTypesLists">
              <bk-option
                :id="item[0]"
                :name="item[0]">
                {{ item[0] }}
              </bk-option>
            </template>
          </bk-select>
          <jb-input
            class="defult-work_space_script_type"
            disabled
            :style="{ width: '350px' }"
            :value="workDirprefix" />
          <!--  @on-focus="(value) => handlerFocus('command', value)" @on-blur="(value) => handlerBlur('command', value)" -->
          <!-- `${workSpaceScriptTypeTips}` -->
          <jb-input
            class="work_space_script_type"
            placeholder="请输入脚本文件路径"
            :style="{ width: '100%' }"
            :value="workDirShowVal"
            @change="(value) => handleParamChange('command', value)" />
        </div>
      </jb-form-item>
      <jb-form-item
        v-show="false"
        class="cus-form-item"
        label="队列分区">
        <partition-select
          :value="formData.partition"
          @change="(value) => handleParamChange('partition', value)" />
      </jb-form-item>
      <jb-form-item
        class="cus-form-item item-tipsed"
        label="计算节点数">
        <div class="item-tips">
          <jb-input-number
            :max="maxNodes"
            :min="0"
            placeholder="参与计算任务的计算节点个数,默认0,不可以超过实际可用节点数量"
            step="1"
            :value="formData.nodes"
            @change="(value) => handleParamChange('nodes', value)" />
          <span class="tip-inner">节点总数:<em>{{ maxNodes }}</em></span>
        </div>
      </jb-form-item>
      <jb-form-item
        class="cus-form-item cpu-item"
        label="CPU">
        <div class="item-tips">
          <jb-input-number
            :max="totalCpuNum"
            :min="0"
            placeholder="参与计算任务的 CPU 个数,默认 0,不可以超过实际可用 CPU 数量"
            step="1"
            :value="formData.numCpus"
            @change="(value) => handleParamChange('numCpus', value)" />
          <span class="tip-inner">空闲CPU数:<em>{{ maxCpuNum }}</em></span>
          <span class="tip-inner">CPU总数:<em>{{ totalCpuNum }}</em></span>
        </div>
      </jb-form-item>
      <!--  item-tipsed -->
      <jb-form-item
        class="cus-form-item"
        label="GPU">
        <div class="item-tips">
          <jb-input-number
            :min="0"
            placeholder="参与计算任务的 GPU 个数,默认 0,不可以超过实际可用 GPU 数量"
            step="1"
            type="number"
            :value="formData.numGpus"
            @change="(value) => handleParamChange('numGpus', value)" />
          <!-- <span class="tip-inner">空闲GPU数:<em>{{ maxCpuNum }}</em></span> -->
        </div>
      </jb-form-item>
      <jb-form-item
        v-show="false"
        class="cus-form-item"
        label="Slurm用户">
        <jb-input
          disabled
          placeholder="Slurm 系统内的用户,默认 root"
          :value="formData.userId"
          @change="(value) => handleParamChange('userId', value)" />
      </jb-form-item>
      <jb-form-item
        class="cus-form-item"
        label="计算时间限制">
        <bk-input
          ref="input"
          :max="259200"
          :min="0"
          placeholder="参与计算任务的时间限制,默认无"
          :precision="0"
          type="number"
          :value="formData.timeLimit"
          @change="(value) => handleParamChange('timeLimit', value)">
          <template slot="append">
            <div class="group-text">
              s
            </div>
          </template>
        </bk-input>
      </jb-form-item>
      <jb-form-item
        class="cus-form-item log-path-item"
        label="任务日志路径">
        <div class="item-tips">
          <!--  @on-focus="(value) => handlerFocus('logPath', value)" @on-blur="(value) => handlerBlur('logPath', value)" -->
          <jb-input
            disabled
            placeholder="计算任务日志保存路径,默认无"
            :value="logDirIsFocused ? logDirShowVal : formData.logPath"
            @change="(value) => handleParamChange('logPath', value)" />
          <span class="tip-inner">根据步骤名称自动生成</span>
        </div>
      </jb-form-item>
    </div>
  </div>
</template>
<script>
  import _ from 'lodash';
  import { getUserInfor } from '@utils/assist';
  import PublicScriptManageService from '@service/public-script-manage';
  import ScriptManageService from '@service/script-manage';
  import SlurmJobService from '@service/slurm';

  import TaskStepModel from '@model/task/task-step';

  import { genDefaultWorkDir, workDirName } from '@utils/assist';

  import ComposeFormItem from '@components/compose-form-item';
  import GpuTypesSelect from '@components/gpu-types-select';
  import PartitionSelect from '@components/gpu-types-select/partition';

  import I18n from '@/i18n';

  const ScriptTypes = () => [['bash', '.sh'], ['python', '.py'], ['python3', '.py']];
  export default {
    components: {
      ComposeFormItem,
      GpuTypesSelect,
      PartitionSelect,
    },
    props: {
      // 脚本来源字段名
      scriptSourceField: {
        type: String,
        required: true,
      },
      // 脚本id字段名
      scriptIdField: {
        type: String,
        required: true,
      },
      // 脚本版本id字段名
      scriptVersionIdField: {
        type: String,
      },
      // 脚本内容字段名
      contentField: {
        type: String,
        required: true,
      },
      languageField: {
        type: String,
        required: true,
      },
      // 脚本语言字段名
      scriptStatusField: {
        type: String,
        required: true,
      },
      formData: {
        type: Object,
        default: () => ({}),
      },
      scriptSource: {
        type: Number,
        default: 1,
      },
    },
    data() {
      return {
        defaultScript: {},
        scriptGroup: [],
        publicScriptGroup: [],
        referType: 2,
        maxNodes: 0,
        maxCpuNum: 0,
        totalCpuNum: 0,
        maxGpuNum: 99999,
        // 工作路径相关设置
        scriptTypesLists: ScriptTypes(),
        workSpaceScriptType: 'bash',
        workSpaceScriptTypeTips: 'bash /tmp/demo/cpu_task.sh',
        workDirprefix: genDefaultWorkDir(),
        workDirShowVal: '',
        workDirIsFocused: false,
        // 日志路径
        logDirprefix: `${genDefaultWorkDir()}logs/`,
        logDirShowVal: '',
        logDirIsFocused: false,
      };
    },
    computed: {
      /**
       * @desc 使用脚本资源需要的权限
       */
      authView() {
        return this.formData[this.scriptSourceField] === TaskStepModel.scriptStep.TYPE_SOURCE_BUSINESS
          ? 'script/view'
          : 'public_script/view';
      },
      /**
       * @desc 脚本新建的权限
       * @returns { String }
       */
      authCreate() {
        return this.formData[this.scriptSourceField] === TaskStepModel.scriptStep.TYPE_SOURCE_BUSINESS
          ? 'script/create'
          : 'public_script/create';
      },
      /**
       * @desc 脚本下拉列表显示格式
       * @returns { String }
       */
      scriptGroupComponent() {
        if (this.scriptListDisplay.length > 1) {
          return 'bk-option-group';
        }
        return 'div';
      },
      /**
       * @desc 引用脚本类型
       * @returns { Boolean }
       */
      isScriptRefer() {
        return this.formData[this.scriptSourceField] !== TaskStepModel.scriptStep.TYPE_SOURCE_LOCAL;
      },
      /**
       * @desc 脚本列表
       * @returns { Array }
       */
      scriptListDisplay() {
        const scriptSource = this.formData[this.scriptSourceField];

        if (scriptSource === TaskStepModel.scriptStep.TYPE_SOURCE_BUSINESS) {
          return this.scriptGroup;
        }
        if (scriptSource === TaskStepModel.scriptStep.TYPE_SOURCE_PUBLIC) {
          return this.publicScriptGroup;
        }
        return [];
      },
      /**
       * @desc 按钮的文本
       * @returns { String }
       */
      newBtnText() {
        return this.formData[this.scriptSourceField] === TaskStepModel.scriptStep.TYPE_SOURCE_BUSINESS
          ? I18n.t('新建业务脚本')
          : I18n.t('新建公共脚本');
      },
      /**
       * @desc 表单想验证规则
       * @returns { Array }
       *
       */
      rules() {
        if (this.isScriptRefer) {
          return [{
            required: true,
            message: I18n.t('请选择引用脚本'),
            trigger: 'blur',
          }];
        }
        return [];
      },
      commandRules() {
        return [{
                  required: true,
                  message: '请输入工作脚本完整路径',
                  trigger: 'blur',
                },
                {
                  validator: this.validatorCommand,
                  message: '请输入脚本类型对应正确的文件路径',
                  trigger: 'blur',
                }, {
                  validator: this.validatorCommandVer,
                  message: '请输入正确的文件路径',
                  trigger: 'blur',
                },
        ];
      },
    },
    watch: {
      formData: {
        handler() {
          this.initScriptSource();
        },
        immediate: true,
      },
      'formData.scriptId'(value) {
        if (value) {
          this.$refs.scriptId.clearValidator();
        }
      },
      // 需要同步的脚本默认展开脚本列表
      scriptListDisplay() {
        if (this.scriptListDisplay.length > 1 && !this.hasShowScriptSelect) {
          setTimeout(() => {
            this.hasShowScriptSelect = true;
            this.$refs.scriptSelect.$el.querySelector('.bk-select-name').click();
          }, 30);
        }
      },

    },
    created() {
      this.scriptListMemo = [];
      this.publicScriptListMemo = [];
      this.initScriptContent();
      this.fetchScriptList();
      this.fetchPublicScriptList();

      this.hasShowScriptSelect = false;

      this.initSlurm();
    },
    methods: {
      /**
       * @desc 获取业务脚本列表
       */
      fetchScriptList() {
        ScriptManageService.getOnlineScriptList()
          .then((data) => {
            this.scriptGroup = [data];
            this.scriptListMemo = data;
            this.composeGroup();
          });
      },
      /**
       * @desc 获公共脚本列表
       */
      fetchPublicScriptList() {
        PublicScriptManageService.getOnlineScriptList()
          .then((data) => {
            this.publicScriptGroup = [data];
            this.publicScriptListMemo = data;
            this.composeGroup();
          });
      },
      /**
       * @desc 获取指定的脚本版本数据
       * @param {Object} params 脚本数据
       */
      fetchScriptVersionDetail(params) {
        return ScriptManageService.versionDetail(params);
      },
      /**
       * @desc 初始化脚本来源
       */
      initScriptSource() {
        // 如果是引用脚本，还需初始化引用类型
        this.referType = this.formData[this.scriptSourceField];
        const {scopeName:SN,username:UN}=getUserInfor();
        const { baseInfo, command } = this.formData;

        const { scopeName=SN, modelByUser=UN,} = baseInfo || {};

        this.workDirprefix = `${workDirName}${scopeName}/${modelByUser}/`;
        this.workDirShowVal = command.split(`${this.workDirprefix}`)[1];

        this.logDirprefix = `${workDirName}${scopeName}/${modelByUser}/logs/`;
      },
      /**
       * @desc 初始化脚本内容
       */
      initScriptContent() {
        // 有默认的脚本版本，获取对应版本的脚本内容
        if (!this.formData[this.scriptIdField] && !this.formData[this.scriptVersionIdField]) {
          return;
        }
        this.$emit('on-reset', {
          isScriptContentLoading: true,
        });
        this.fetchScriptVersionDetail({
          id: this.formData[this.scriptVersionIdField],
        }).then((script) => {
          const { content, publicScript } = script;
          const scriptSource = publicScript
            ? TaskStepModel.scriptStep.TYPE_SOURCE_PUBLIC
            : TaskStepModel.scriptStep.TYPE_SOURCE_BUSINESS;
          this.defaultScript = script;
          this.composeGroup();
          this.$emit('on-reset', {
            [this.contentField]: content,
            [this.scriptSourceField]: scriptSource,
          });
        })
          .finally(() => {
            this.$emit('on-reset', {
              isScriptContentLoading: false,
            });
          });
      },
      /**
       * @desc 脚本版本有更新时需要将最新的脚本版本组合到脚本列表中
       */
      composeGroup() {
        if (!this.defaultScript.id) {
          return;
        }
        const { publicScript } = this.defaultScript;
        const targetList = publicScript
          ? _.cloneDeep(this.publicScriptListMemo)
          : _.cloneDeep(this.scriptListMemo);
        if (targetList.length < 1) {
          return;
        }
        if (!this.formData[this.scriptStatusField]) {
          this.scriptGroup = Object.freeze([this.scriptListMemo]);
          this.publicScriptGroup = Object.freeze([this.publicScriptListMemo]);
          return;
        }
        const currentScriptList = [];
        const oldVersionScript = {
          ...this.defaultScript,
          name: `${this.defaultScript.name}（${I18n.t('当前版本')}）`,
        };
        currentScriptList.push(oldVersionScript);
        const [newVersionScript] = _.remove(targetList, item => item.id === oldVersionScript.id);
        if (newVersionScript) {
          newVersionScript.name = `${newVersionScript.name}（${I18n.t('新版本')}）`;
          currentScriptList.unshift(newVersionScript);
        }

        if (publicScript) {
          this.scriptGroup = Object.freeze([this.scriptListMemo]);
          this.publicScriptGroup = Object.freeze([currentScriptList, targetList]);
        } else {
          this.scriptGroup = Object.freeze([currentScriptList, targetList]);
          this.publicScriptGroup = Object.freeze([this.publicScriptListMemo]);
        }
      },

      /**
       * @desc 更新脚本引用来源类型
       * @param {String} scriptSource 脚本引用来源类型
       */
      handleReferScriptTypeChange(scriptSource) {
        console.log('******handleReferScriptTypeChange******');

        this.$emit('on-reset', {
          [this.scriptSourceField]: scriptSource,
          [this.scriptIdField]: '',
          [this.scriptVersionIdField]: '',
        });
      },
      /**
       * @desc 更新脚本引用版本
       * @param {String} scriptVersionId 脚本引用来源类型
       */
      handleScriptVersionIdChange(scriptVersionId) {
        if (!scriptVersionId) {
          return;
        }
        this.$emit('on-reset', {
          isScriptContentLoading: true,
        });
        this.fetchScriptVersionDetail({
          id: scriptVersionId,
        }).then(({ id, type, content }) => {
          this.$emit('on-reset', {
            [this.scriptIdField]: id,
            [this.contentField]: content,
            [this.languageField]: type,
            [this.scriptVersionIdField]: scriptVersionId,
            [this.scriptStatusField]: 0,
            isScriptContentLoading: false,
          });
        })
          .finally(() => {
            this.$emit('on-reset', {
              isScriptContentLoading: false,
            });
          });
      },
      /**
       * @desc 跳转到选择的脚本版本详情
       */
      handleGoScriptDetail() {
        const routerName = this.formData[this.scriptSourceField] === TaskStepModel.scriptStep.TYPE_SOURCE_PUBLIC
          ? 'publicScriptVersion'
          : 'scriptVersion';

        const router = this.$router.resolve({
          name: routerName,
          params: {
            id: this.formData[this.scriptIdField],
          },
          query: {
            scriptVersionId: this.formData[this.scriptVersionIdField],
          },
        });
        window.open(router.href);
      },
      /**
       * @desc 跳转新建脚本页面
       */
      handleGoCreate() {
        const routerName = this.formData[this.scriptSourceField] === TaskStepModel.scriptStep.TYPE_SOURCE_PUBLIC
          ? 'createPublicScript'
          : 'createScript';

        const { href } = this.$router.resolve({
          name: routerName,
        });

        window.open(href);
      },
      handleParamChange(key, value) {
        let val = value;
        switch (key) {
        case 'command':
          val = `${this.workSpaceScriptType} ${this.workDirprefix}${val[0] == '/' ? val.replace('/', '') : val}`;
          break;
        case 'logPath':
          val = `${this.logDirprefix}${val}`;
          break;
        default:
          break;
        }
        this.$emit('on-change', key, val);
      },
      handleWorkSpaceScriptTypeChange(e) {
        this.workSpaceScriptType = e;
        const nameItem = this.scriptTypesLists.filter(item => item[0] == e)[0];
        this.workSpaceScriptTypeTips = `${nameItem[0]} /tmp/demo/cpu_task${nameItem[1]}`;

        // 重置 command
        const [_, val] = this.formData.command.split(this.workDirprefix);
        this.handleParamChange('command', `${val}`);
        this.$emit('on-change', 'workSpaceScriptType', e);
      },
      async initSlurm() {
        const prefixes = ScriptTypes().map(item => item[0]);
        if (prefixes.some(prefix => this.formData.command.startsWith(prefix))) {
          this.workSpaceScriptType = this.formData.command.split(' ')[0];
        } else {
          this.workSpaceScriptType = this.formData.workSpaceScriptType || 'bash';
        }

        await SlurmJobService.getNodes().then(({ data }) => {
          this.maxNodes = parseInt(data.nodes) || 0;
        });

        await SlurmJobService.getCpuNums().then(({ data }) => {
          this.maxCpuNum = parseInt(data.free_cpus) / this.maxNodes || 0;
          this.totalCpuNum = parseInt(data.total_cpus) / this.maxNodes || 0;
        });
      },
      handlerBlur(key, value) {
        switch (key) {
        case 'command':
          this.workDirShowVal = value;
          this.workDirIsFocused = false;
          break;

        case 'logPath':
          this.logDirShowVal = value;
          this.logDirIsFocused = false;
          break;

        default:
          break;
        }
      },
      handlerFocus(key, value) {
        switch (key) {
        case 'command':
          this.getFix(key);
          this.workDirIsFocused = true;
          break;

        case 'logPath':
          this.getFix(key);
          this.logDirIsFocused = true;
          break;

        default:
          break;
        }
      },
      getFix(key) {
        let splitVal;
        switch (key) {
        case 'command':
          splitVal = this.formData[key].split(this.workDirprefix);
          this.workDirShowVal = splitVal.length == 2 ? this.workDirShowVal = splitVal[1] : '';
          break;
        case 'logPath':
          splitVal = this.formData[key].split(this.logDirprefix);
          this.logDirShowVal = splitVal.length == 2 ? this.logDirShowVal = splitVal[1] : '';
          break;
        default:
          break;
        }
      },
      validatorCommand(value) {
        const [type, suffixVal] = this.scriptTypesLists.filter(item => item[0] == this.workSpaceScriptType)[0];
        if (suffixVal == '') return value.endsWith('/');
        const isFile = suffixVal.split('.');
        if (value.endsWith(suffixVal)) {
          const val = value.split('/').pop()
            .split('.');
          return val[0] != '';
        }
        return false;
      },
      validatorCommandVer(value) {
        const [_, val] = value.split(this.workDirprefix);
        if (this.workDirprefix.endsWith('/')) {
          const [_val, val1] = value.split(this.workDirprefix.slice(0, -1));
          if (this.hasMultipleLeadingSlashes(val1)) return false;
        }
        if (this.hasMultipleLeadingSlashes(val)) return false;
        const _val = val.replace(/^\/+/, '');
        return /^(\/[^\/\0]+)*(\/[^\/\0]+)*(\.[a-zA-Z0-9]+)?$/.test(`/${_val}`);
      },
      hasMultipleLeadingSlashes(str) {
        return str.length >= 2 && str[0] === '/' && str[1] === '/';
      },
    },
  };
</script>
<style lang='postcss'>
.script-source-of-template {
  .script-source-item {
    .bk-radio-button-text {
      width: 120px;
      text-align: center;
    }
  }

  .update-flag {
    margin-left: 4px;
    color: #ff5656;
  }

  .refer-script-item {
    position: relative;
    display: flex;
    align-items: center;

    .refer-script-detail {
      display: flex;
      align-items: center;
      justify-content: center;
      margin-left: 4px;
      font-size: 16px;
      color: var(--color-primary);
      cursor: pointer;
    }
  }
}

.cus-form-item {
  width: 495px;

  &.cpu-item {
    width: 695px !important;
  }

  &.log-path-item {
    width: 100% !important;

    .jb-input {
      width: calc(100% - 105px);
    }
  }

  &.item-tipsed {
    width: 610px;
  }

  .item-tips {
    width: 100%;
    display: flex;

    .tip-inner {
      display: inline-block;
      width: 140px;
      margin-left: 10px;
      vertical-align: middle;
      font-size: 12px;
      color: #63656e;

      em {
        color: #2DCB8D
      }
    }
  }
}

.is-error {
  .refer-script-item {
    .work_space_script_type-select {
      .bk-select-name {
        height: 31px;
      }
    }

    .work_space_script_type {}

    .defult-work_space_script_type {
      input {
        border-left: none;
        border-right: none;
        border-color: var(--error-color-base) !important;
        color: var(--text-color-primary) !important;
      }
    }
  }
}
</style>
